package com.patrick.open.api.core.exception;

import com.patrick.open.api.core.common.RespCode;
import com.patrick.open.api.core.common.RespData;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.server.ResponseStatusException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author GP
 * @date 2019/12/11 2:31 PM
 */
@RestControllerAdvice
@Slf4j
public class OpenGlobalExceptionHandler {


  /**
   * 自定义异常
   *
   * @param ex
   * @param request
   * @param response
   * @return
   */
  @ExceptionHandler({OpenException.class})
  public static RespData openException(Exception ex, HttpServletRequest request, HttpServletResponse response) {
    RespData resultBody = resolveException(ex, request.getRequestURI());
    response.setStatus(resultBody.getHttpStatus());
    return resultBody;
  }

  /**
   * 统一异常处理
   *
   * @param ex
   * @param request
   * @param response
   * @return
   */
  @ResponseStatus(value = HttpStatus.OK)
  @ExceptionHandler({AuthenticationException.class})
  public static RespData authenticationException(Exception ex, HttpServletRequest request, HttpServletResponse response) {
    RespData resultBody = resolveException(ex, request.getRequestURI());
    response.setStatus(resultBody.getHttpStatus());
    return resultBody;
  }


  /**
   * mp异常
   *
   * @param ex
   * @param request
   * @param response
   * @return
   */
  @ExceptionHandler(value = MyBatisSystemException.class)
  @ResponseStatus(value = HttpStatus.OK)
  public static RespData myBatisSystemException(Exception ex, HttpServletRequest request, HttpServletResponse response) {
    RespData resultBody = resolveException(ex, request.getRequestURI());
    response.setStatus(resultBody.getHttpStatus());
    return resultBody;
  }

  /**
   * oauth2.0异常处理
   *
   * @param ex
   * @param request
   * @param response
   * @return
   */
  @ExceptionHandler({OAuth2Exception.class, InvalidTokenException.class})
  public static RespData ResultBodRespDataxception(Exception ex, HttpServletRequest request, HttpServletResponse response) {
    RespData respData = resolveException(ex, request.getRequestURI());
    response.setStatus(respData.getHttpStatus());
    return respData;
  }

  public static RespData resolveException(Exception ex, String path) {
    RespCode code = RespCode.ERROR;
    int httpStatus = HttpStatus.INTERNAL_SERVER_ERROR.value();
    String message = ex.getMessage();
    String superClassName = ex.getClass().getSuperclass().getName();
    String className = ex.getClass().getName();

    if (className.contains("ResponseStatusException")) {
      httpStatus = ((ResponseStatusException) ex).getStatus().value();
      code = RespCode.ERROR;
    } else if (className.contains("UsernameNotFoundException")) {
      httpStatus = HttpStatus.UNAUTHORIZED.value();
      code = RespCode.USERNAME_NOT_FOUND;
    } else if (className.contains("BadCredentialsException")) {
      httpStatus = HttpStatus.UNAUTHORIZED.value();
      code = RespCode.BAD_CREDENTIALS;
    } else if (className.contains("AccountExpiredException")) {
      httpStatus = HttpStatus.UNAUTHORIZED.value();
      code = RespCode.ACCOUNT_EXPIRED;
    } else if (className.contains("LockedException")) {
      httpStatus = HttpStatus.UNAUTHORIZED.value();
      code = RespCode.ACCOUNT_LOCKED;
    } else if (className.contains("DisabledException")) {
      httpStatus = HttpStatus.UNAUTHORIZED.value();
      code = RespCode.ACCOUNT_DISABLED;
    } else if (className.contains("CredentialsExpiredException")) {
      httpStatus = HttpStatus.UNAUTHORIZED.value();
      code = RespCode.CREDENTIALS_EXPIRED;
    } else if (className.contains("InvalidClientException")) {
      httpStatus = HttpStatus.UNAUTHORIZED.value();
      code = RespCode.INVALID_CLIENT;
    } else if (className.contains("UnauthorizedClientException")) {
      httpStatus = HttpStatus.UNAUTHORIZED.value();
      code = RespCode.UNAUTHORIZED_CLIENT;
    } else if (className.contains("InsufficientAuthenticationException") || className.contains("AuthenticationCredentialsNotFoundException")) {
      httpStatus = HttpStatus.UNAUTHORIZED.value();
      code = RespCode.UNAUTHORIZED;
    } else if (className.contains("InvalidGrantException")) {
      code = RespCode.ALERT;
      if ("Bad credentials".contains(message)) {
        code = RespCode.BAD_CREDENTIALS;
      } else if ("User is disabled".contains(message)) {
        code = RespCode.ACCOUNT_DISABLED;
      } else if ("User account is locked".contains(message)) {
        code = RespCode.ACCOUNT_LOCKED;
      }
    } else if (className.contains("InvalidScopeException")) {
      httpStatus = HttpStatus.UNAUTHORIZED.value();
      code = RespCode.INVALID_SCOPE;
    } else if (className.contains("InvalidTokenException")) {
      httpStatus = HttpStatus.UNAUTHORIZED.value();
      code = RespCode.INVALID_TOKEN;
    } else if (className.contains("InvalidRequestException")) {
      httpStatus = HttpStatus.BAD_REQUEST.value();
      code = RespCode.INVALID_REQUEST;
    } else if (className.contains("RedirectMismatchException")) {
      code = RespCode.REDIRECT_URI_MISMATCH;
    } else if (className.contains("UnsupportedGrantTypeException")) {
      code = RespCode.UNSUPPORTED_GRANT_TYPE;
    } else if (className.contains("UnsupportedResponseTypeException")) {
      code = RespCode.UNSUPPORTED_RESPONSE_TYPE;
    } else if (className.contains("UserDeniedAuthorizationException")) {
      code = RespCode.ACCESS_DENIED;
    } else if (className.contains("AccessDeniedException")) {
      code = RespCode.ACCESS_DENIED;
      httpStatus = HttpStatus.FORBIDDEN.value();
      if (RespCode.ACCESS_DENIED_BLACK_LIMITED.getMsg().contains(message)) {
        code = RespCode.ACCESS_DENIED_BLACK_LIMITED;
      } else if (RespCode.ACCESS_DENIED_WHITE_LIMITED.getMsg().contains(message)) {
        code = RespCode.ACCESS_DENIED_WHITE_LIMITED;
      } else if (RespCode.ACCESS_DENIED_AUTHORITY_EXPIRED.getMsg().contains(message)) {
        code = RespCode.ACCESS_DENIED_AUTHORITY_EXPIRED;
      } else if (RespCode.ACCESS_DENIED_UPDATING.getMsg().contains(message)) {
        code = RespCode.ACCESS_DENIED_UPDATING;
      } else if (RespCode.ACCESS_DENIED_DISABLED.getMsg().contains(message)) {
        code = RespCode.ACCESS_DENIED_DISABLED;
      } else if (RespCode.ACCESS_DENIED_NOT_OPEN.getMsg().contains(message)) {
        code = RespCode.ACCESS_DENIED_NOT_OPEN;
      }
    } else if (className.contains("HttpMessageNotReadableException")
      || className.contains("TypeMismatchException")
      || className.contains("MissingServletRequestParameterException")) {
      httpStatus = HttpStatus.BAD_REQUEST.value();
      code = RespCode.BAD_REQUEST;
    } else if (className.contains("NoHandlerFoundException")) {
      httpStatus = HttpStatus.NOT_FOUND.value();
      code = RespCode.NOT_FOUND;
    } else if (className.contains("HttpRequestMethodNotSupportedException")) {
      httpStatus = HttpStatus.METHOD_NOT_ALLOWED.value();
      code = RespCode.METHOD_NOT_ALLOWED;
    } else if (className.contains("HttpMediaTypeNotAcceptableException")) {
      httpStatus = HttpStatus.BAD_REQUEST.value();
      code = RespCode.MEDIA_TYPE_NOT_ACCEPTABLE;
    } else if (className.contains("MethodArgumentNotValidException")) {
      BindingResult bindingResult = ((MethodArgumentNotValidException) ex).getBindingResult();
      code = RespCode.ALERT;
      return RespData.failed().code(code.getCode()).msg(bindingResult.getFieldError().getDefaultMessage());
    } else if (className.contains("IllegalArgumentException")) {
      //参数错误
      code = RespCode.ALERT;
      httpStatus = HttpStatus.BAD_REQUEST.value();
    } else if (className.contains("OpenAlertException")) {
      code = RespCode.ALERT;
    } else if (className.contains("OpenSignatureException")) {
      httpStatus = HttpStatus.BAD_REQUEST.value();
      code = RespCode.SIGNATURE_DENIED;
    } else if (message.equalsIgnoreCase(RespCode.TOO_MANY_REQUESTS.name())) {
      code = RespCode.TOO_MANY_REQUESTS;
    } else if (className.contains("MyBatisSystemException")) {
      code = RespCode.SERVICE_DEAL_ERROR;
    }
    return buildBody(ex, code, path, httpStatus);
  }

  /**
   * 构建返回结果对象
   *
   * @param exception
   * @return
   */
  private static RespData buildBody(Exception exception, RespCode resultCode, String path, int httpStatus) {
    if (resultCode == null) {
      resultCode = RespCode.ERROR;
    }
    RespData respData = RespData.failed().code(resultCode.getCode()).msg(exception.getMessage()).path(path).httpStatus(httpStatus);
    log.error("==> error:{} exception: {}", respData, exception);
    return respData;
  }
}
